﻿# -*- coding: utf-8 -*-
# TaoSyncGUI.py

# Copyright 2012 Ryan Schultz

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

import urllib2
import time
import threading
import ConfigParser
import os
from subprocess import Popen

import wx

import Manifest
import TaoSync

sync_icon = """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"""

check_icon = """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
AAAASUVORK5CYII="""

arma_icon = """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"""

import base64

sync_icon_dec = base64.b64decode(sync_icon)
fout = open("sync_icon-64.png","wb")
fout.write(sync_icon_dec)
fout.close()

check_icon_dec = base64.b64decode(check_icon)
fout = open("check_icon-64.png","wb")
fout.write(check_icon_dec)
fout.close()

arma_icon_dec = base64.b64decode(arma_icon)
fout = open("arma_icon-64.png","wb")
fout.write(arma_icon_dec)
fout.close()

class ConfigExeDialog(wx.Dialog):
	def __init__(self, flags_str, custommods_str, *args, **kwargs):
		super(ConfigExeDialog, self).__init__(*args, **kwargs) 
		style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
		self.SetTitle("Configure Launch Options")
		
		self.flags_str = flags_str
		self.custommods_str = custommods_str
		
		vbox = wx.BoxSizer(wx.VERTICAL)
				
		cap1 = wx.StaticText(self, -1, "Launch flags (format: -nosplash -nopause)")
		vbox.Add(cap1, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		self.flags = wx.TextCtrl(self, -1)
		self.flags.SetValue(self.flags_str)
		vbox.Add(self.flags, flag = wx.EXPAND|wx.ALL, border = 8)
		
		cap2 = wx.StaticText(self, -1, "Custom mods (format: @addon1;@addon2;@addon3;)")
		vbox.Add(cap2, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		self.custommods = wx.TextCtrl(self, -1)
		self.custommods.SetValue(self.custommods_str)
		vbox.Add(self.custommods, flag = wx.EXPAND|wx.ALL, border = 8)
		
		buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
		#self.ok = self.FindWindowById(wx.ID_OK)
		#self.cancel = self.FindWindowById(wx.ID_NO)
		vbox.Add(buttons, flag = wx.EXPAND|wx.ALL, border = 8)
		
		self.SetSizerAndFit(vbox)
		
		self.Bind(wx.EVT_BUTTON, self.OnOk, id = wx.ID_OK)
		self.Bind(wx.EVT_BUTTON, self.OnClose, id = wx.ID_CANCEL)
		
		self.Bind(wx.EVT_CLOSE, self.OnClose)
	
	def GetResult(self):
		flags_str = self.flags.GetValue()
		mods_str = self.custommods.GetValue()
		return (flags_str, mods_str)
	
	def OnOk(self,e):
		
		
		self.EndModal(wx.ID_OK)
		
		self.Destroy()
		
				
	def OnClose(self, e):
		self.EndModal(wx.ID_CANCEL) 
		self.Destroy()
				

class BuildRepoDialog(wx.Dialog):
	def __init__(self, *args, **kwargs):
		super(BuildRepoDialog, self).__init__(*args, **kwargs) 
		style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
		self.SetTitle("Build Repository")
		vbox = wx.BoxSizer(wx.VERTICAL)
		
		desc = wx.StaticText(self, -1, "Enter the following information to build a new repository.\nThis directory should contain extracted ArmA 2 mods in their @folder structure, just as if\nyou were putting them into ArmA 2's directory.\n\nAfter building, you can then upload the repository folder to your specified web address and \nit will be ready to use with TaoSync.")
		vbox.Add(desc, flag = wx.EXPAND|wx.ALL, border = 8)
		#vbox.InsertSpacer((20,20))
		
		cap1 = wx.StaticText(self, -1, "Repository name")
		vbox.Add(cap1, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		self.name_box = wx.TextCtrl(self, -1)
		vbox.Add(self.name_box, flag = wx.EXPAND|wx.ALL, border = 8)
		
		cap2 = wx.StaticText(self, -1, "Repository web root address (http://www.example.com/examplerepo/)")
		vbox.Add(cap2, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		self.web_box = wx.TextCtrl(self, -1)
		vbox.Add(self.web_box, flag = wx.EXPAND|wx.ALL, border = 8)
		
		
		cap3 = wx.StaticText(self, -1, "Repository local folder")
		vbox.Add(cap3, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		fdbox = wx.BoxSizer(wx.HORIZONTAL)
		self.path_box = wx.TextCtrl(self, -1, style = wx.TE_READONLY)
		fdbox.Add(self.path_box, proportion = 1, flag = wx.EXPAND|wx.ALL)
		self.path_button = wx.BitmapButton(self, -1, bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN), style = wx.BU_EXACTFIT|wx.BU_AUTODRAW)
		fdbox.Add(self.path_button, proportion = 0, flag = wx.ALIGN_RIGHT)
		
		vbox.Add(fdbox, flag = wx.EXPAND|wx.ALL, border = 8)
		
		cap4 = wx.StaticText(self, -1, "Save manifest file to")
		vbox.Add(cap4, flag = wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border = 8)
		
		mdbox = wx.BoxSizer(wx.HORIZONTAL)
		self.mani_box = wx.TextCtrl(self, -1, style = wx.TE_READONLY)
		mdbox.Add(self.mani_box, proportion = 1, flag = wx.EXPAND|wx.ALL)
		self.mani_button = wx.BitmapButton(self, -1, bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN), style = wx.BU_EXACTFIT|wx.BU_AUTODRAW)
		mdbox.Add(self.mani_button, proportion = 0, flag = wx.ALIGN_RIGHT)
		
		vbox.Add(mdbox, flag = wx.EXPAND|wx.ALL, border = 8)
		
		
		buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
		#self.ok = self.FindWindowById(wx.ID_OK)
		#self.cancel = self.FindWindowById(wx.ID_NO)
		vbox.Add(buttons, flag = wx.EXPAND|wx.ALL, border = 8)
		
		self.SetSizerAndFit(vbox)
		
		self.Bind(wx.EVT_BUTTON, self.OnDirChooser, self.path_button)
		self.Bind(wx.EVT_BUTTON, self.OnManiChooser, self.mani_button)
		self.Bind(wx.EVT_BUTTON, self.OnOk, id = wx.ID_OK)
		self.Bind(wx.EVT_BUTTON, self.OnClose, id = wx.ID_CANCEL)
		
		self.Bind(wx.EVT_CLOSE, self.OnClose)
		
	def GetName(self):
		return self.name_box.GetValue()
	def GetAddress(self):
		return self.web_box.GetValue()
	def GetDir(self):
		return self.path_box.GetValue()
	def GetManifest(self):
		return self.mani_box.GetValue()
        
	def OnOk(self,e):
		repo_name = self.GetName()
		repo_address = self.GetAddress()
		repo_dir = self.GetDir()
		repo_manifest = self.GetManifest()
		
		if repo_name == None:
			repo_name = "Untitled Repository"
		if repo_address == None:
			repo_address = "http://"
		
		print "repo_dir", repo_dir
		
		if len(repo_dir) == 0 or len(repo_manifest) == 0:
			wx.MessageBox("You must provide both a directory and a location to save the manifest XML file.", "Error", wx.OK | wx.ICON_ERROR)
		else:
			pd = wx.ProgressDialog(title = "Building repository", message = "Building repository for %s, please wait..." % repo_name, style = wx.PD_SMOOTH | wx.PD_ELAPSED_TIME)
			pd.Pulse()
		
			try:
				manifestaddress_string = repo_address + os.path.basename(repo_manifest)
				mf = Manifest.Manifest(address = repo_address, manifestaddress = repo_manifest, servername = repo_name)
				mf.make_file_list(repo_dir)
				build_thread = threading.Thread(target=mf.build_manifest, args=(repo_manifest,))
				build_thread.start()
				build_wait_thread = threading.Thread(target=self.wait_to_finish_build, args=(build_thread,pd))
				build_wait_thread.start()
			except:
				wx.MessageBox("Unable to build repository. Check your files and try again.", "Error", wx.OK | wx.ICON_ERROR)
				pd.Destroy()
				raise
				
			
		
	def wait_to_finish_build(self, build_thread, pd):
		build_thread.join()
		# Will block until sync finishes
		pd.Destroy()
		wx.MessageBox("Repository built at %s." % self.GetDir(), "Success", wx.OK | wx.ICON_INFORMATION)
		
	def OnClose(self, e):
		self.EndModal(wx.ID_CANCEL)
		self.Destroy()
		
	def OnDirChooser(self, e):
		dirdialog = wx.DirDialog(self, message = "Select directory to use as a repository. This should contain ArmA 2 @addon folders.", defaultPath = os.getcwd())
		
		if dirdialog.ShowModal() == wx.ID_OK:
			self.path_box.SetValue(dirdialog.GetPath())
			return dirdialog.GetPath()
		else:
			return None
			
		
	
	def OnManiChooser(self, e):
		filedialog = wx.FileDialog(self, message = 'Save manifest XML file', defaultDir = os.getcwd(), style = wx.FD_SAVE, wildcard = "TaoSync XML files (*.xml)|*.xml|All types (*.*|*)")
		
		if filedialog.ShowModal() == wx.ID_OK:
			self.mani_box.SetValue(filedialog.GetPath())
			return filedialog.GetPath()
		else:
			return None
			
	
class TaoSyncGUI(wx.Frame):
	"""GUI interface for TaoSync, an application to synchronize ArmA mods."""
	def __init__(self, *args, **kwargs):
		super(TaoSyncGUI, self).__init__(*args, **kwargs)
		
		self.version = "0.1alpha"
		
		# Initial configuration stuff
		self.known_servers = {}
		self.selected_manifest = ""
		self.current_server = None
		self.current_selection = 0
		self.is_syncing = False
		self.is_checking = False
		self.ui_blocked = False
		self.ts = None
		self.addon_directory = os.getcwd()
		self.arma_exe = os.path.join(os.getcwd(), "arma2oa.exe")
		self.arma_flags = "-nosplash"
		self.arma_custommods = ""
		
		self.run_dir = os.getcwd()
		
		# Read in configs
		self.read_config()
	
		# Gui stuff follows
		#######################################################3
		
		self.SetBackgroundColour(wx.NullColour)
		self.SetSizeHints(500,367)
		
		icon = wx.EmptyIcon()
		icon.CopyFromBitmap(wx.Bitmap("sync_icon-64.png", wx.BITMAP_TYPE_ANY))
		self.SetIcon(icon)
		
		menubar = wx.MenuBar()
		self.menubar = menubar
		
		syncmenu = wx.Menu()
		addlocal = syncmenu.Append(wx.ID_OPEN, "Add &Local Manifest...")
		self.Bind(wx.EVT_MENU, self.on_addlocal, addlocal)
		addonline = syncmenu.Append(wx.ID_ANY, "Add &Online Manifest...")
		self.Bind(wx.EVT_MENU, self.on_addonline, addonline)
		syncmenu.AppendSeparator()
		exit = syncmenu.Append(wx.ID_EXIT, "E&xit\tCtrl-W")
		self.Bind(wx.EVT_MENU, self.on_exit, exit)
		self.Bind(wx.EVT_CLOSE, self.on_exit)
		
		createmenu = wx.Menu()
		buildrepo = createmenu.Append(wx.ID_ANY, "&Build Repository...")
		self.Bind(wx.EVT_MENU, self.on_buildrepo, buildrepo)
		
		optionsmenu = wx.Menu()
		selectexe = optionsmenu.Append(wx.ID_ANY, "Select &ArmA 2 Executable...")
		self.Bind(wx.EVT_MENU, self.on_selectexe, selectexe)
		configexe = optionsmenu.Append(wx.ID_ANY, "Configure &Launch Options...")
		self.Bind(wx.EVT_MENU, self.on_configexe, configexe)
		selectdir = optionsmenu.Append(wx.ID_ANY, "Select A&ddon Directory...")
		self.Bind(wx.EVT_MENU, self.on_selectdir, selectdir)
		
		
		helpmenu = wx.Menu()
		about = helpmenu.Append(wx.ID_ANY, "About TaoSync")
		self.Bind(wx.EVT_MENU, self.on_about, about)
		
		menubar.Append(syncmenu, '&Sync')
		menubar.Append(createmenu, '&Build')
		menubar.Append(optionsmenu, '&Options')
		menubar.Append(helpmenu, '&Help')
		
		self.SetMenuBar(menubar)
		
		self.toolbar = self.CreateToolBar(style = wx.TB_HORZ_LAYOUT | wx.TB_TEXT)
		tool_sync = self.toolbar.AddLabelTool(1, label = "Sync server", shortHelp = "Sync addons with the selected server.", longHelp = "Sync addons with the selected server.", bitmap = wx.Bitmap("sync_icon-64.png"))
		tool_check = self.toolbar.AddLabelTool(0, label = "Check for updates", shortHelp = "Check all servers for updates.", longHelp = "Check all servers for updates.", bitmap = wx.Bitmap("check_icon-64.png"))
		tool_launch = self.toolbar.AddLabelTool(2, label = "Launch ArmA 2", shortHelp = "Launch ArmA 2 with the addons used by the selected server.", longHelp = "Launch ArmA 2 with the addons used by the selected server.", bitmap = wx.Bitmap("arma_icon-64.png"))
		self.toolbar.Realize()
		
		self.Bind(wx.EVT_MENU, self.on_check, tool_check)
		self.Bind(wx.EVT_MENU, self.on_sync, tool_sync)
		self.Bind(wx.EVT_MENU, self.on_launch, tool_launch)
		
		self.statusbar = self.CreateStatusBar()
		self.statusbar.SetStatusText('Ready.')
		
		vbox = wx.BoxSizer(wx.VERTICAL)
		
		font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
		font.SetPointSize(9)
	
		self.server_listctrl_datamapping = [] # used for data->known_servers map
		self.server_listctrl = wx.ListCtrl(self, style = wx.LC_REPORT, size = (300,200))
		self.server_listctrl.InsertColumn(0,'Server')
		self.server_listctrl.InsertColumn(1,'Synced?')
		self.server_listctrl.InsertColumn(2,'Addons')
		self.server_listctrl.SetColumnWidth(0, 220)
		self.server_listctrl.SetColumnWidth(1, 80)
		self.server_listctrl.SetColumnWidth(2, 280)
		vbox.Add(self.server_listctrl, flag = wx.EXPAND | wx.CENTER, border = 0)
		
		self.popup = wx.Menu()
		self.server_listctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_popup)
		#popup_del = self.popup.Append(-1, "Remove")
		#self.Bind(wx.EVT_MENU, self.on_popupdelete, popup_del)
		#self.server_listctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_popup)
		self.rc_item = None
		
		
		self.progbar = wx.Gauge(self, style = wx.GA_SMOOTH | wx.GA_HORIZONTAL)
		vbox.Add(self.progbar, flag = wx.EXPAND | wx.CENTER)
		self.progbar_dl = wx.Gauge(self, style = wx.GA_SMOOTH | wx.GA_HORIZONTAL, size = (300, 14))
		vbox.Add(self.progbar_dl, flag = wx.EXPAND | wx.CENTER)
		
		self.SetSizer(vbox)
				
		# Update the listbox contents to show stuff from config file
		self.update_listctrl()
		
		if len(self.known_servers) > 0:
			self.server_listctrl.Select(0)
			
		# Change working directory
		try:
			os.chdir(self.addon_directory)
		except:
			print "ERROR: Unable to change to config addon directory. Using current directory."
		
	def on_exit(self, e):
		""" Close the window immediately, then clean up any threads, save the config, then terminate."""
		
		if self.ts:
			self.ts.terminate_download = True
			
		self.write_config()
		
		self.Hide()
		
		# Block and wait until all threads end peacefully
		print "INFO: Waiting for threads to end..."
		#while threading.currentThread() in threading.enumerate():
		#	time.sleep(1)
		print threading.enumerate()
		
		self.Destroy()
	
	def on_popupaddlocal(self, e):
		self.on_addlocal(e)
	def on_popupaddonline(self, e):
		self.on_addonline(e)
	
	def on_popup(self, e):
		pos = e.GetPosition()
		pos = self.server_listctrl.ScreenToClient(pos)
		whats_there = self.server_listctrl.HitTest(pos)
		
		# Clear old menu items
		for item in self.popup.GetMenuItems():
			self.popup.DeleteItem(item)
		
		if whats_there[0] is not -1:
			self.rc_item = whats_there[0]
			popup_del = self.popup.Append(-1, "Remove")
			self.Bind(wx.EVT_MENU, self.on_popupdelete, popup_del)
		else:
			# Clicked in empty space, show options to add
			popup_addlocal = self.popup.Append(-1, "Add Local Manifest...")
			popup_addonline = self.popup.Append(-1, "Add Online Manifest...")
			self.Bind(wx.EVT_MENU, self.on_popupaddlocal, popup_addlocal)
			self.Bind(wx.EVT_MENU, self.on_popupaddonline, popup_addonline)
		
		self.server_listctrl.PopupMenu(self.popup, pos)
			
		
	
	def on_popupdelete(self, e):
		# Get selected item (right click automatically selects)
		selected = self.server_listctrl.GetFirstSelected()
		# Get the data map
		map_idx = self.server_listctrl.GetItemData(selected)
		# Get the server info from the data map
		server, data = self.server_listctrl_datamapping[map_idx]
		server_name = server
		
		confdialog = wx.MessageDialog(None, "Are you sure you want to remove the server %s?" % server_name, caption = "Confirmation", style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
		
		if confdialog.ShowModal() == wx.ID_YES:
			removed = self.known_servers.pop(server_name)
			print "updates list", self.known_servers
			self.server_listctrl.DeleteItem(selected)
			self.update_listctrl()
			self.write_config()
	
		
	def on_addlocal(self, e):
		filedialog = wx.FileDialog(self, message = 'Select manifest file', defaultDir = os.getcwd(), wildcard = "TaoSync XML files (*.xml)|*.xml|All types (*.*|*)")
		
		if filedialog.ShowModal() == wx.ID_OK:
			local_manifest = filedialog.GetPath()
			local_manifest_data = open(local_manifest, "r").read()
		
			self.block_ui()
			self.progbar.SetValue(20)
			
			try:
				mf = Manifest.Manifest(manifestaddress = local_manifest)
				mf.read_manifest(local_manifest_data)
				
				add_manifestaddress = mf.manifestaddress
				if not add_manifestaddress.startswith("file:///"):
					add_manifestaddress = "file:///" + add_manifestaddress
					
				self.known_servers[mf.servername] = [mf.address, add_manifestaddress, -1, mf.addonline]
				
				self.update_listctrl()
				
				self.progbar.SetValue(100)
				self.statusbar.SetStatusText('Manifest added.')
				
				
				self.unblock_ui()
				
				self.write_config()
			except:
				wx.MessageBox("Unable to open manifest file. It may be malformed or corrupted -- try downloading it again.", "Error", wx.OK | wx.ICON_ERROR)
				raise
			finally:
				self.unblock_ui()
			

	def on_addonline(self, e):
		textdialog = wx.TextEntryDialog(self, "Enter the manifest URL:", "Enter manifest URL")
		
		if textdialog.ShowModal() == wx.ID_OK:
			net_manifest = textdialog.GetValue()
			
			self.block_ui()
			self.progbar.SetValue(20)
			
			try:
				print "INFO: Downloading manifest %s..." % net_manifest
								
				net_manifest_data = urllib2.urlopen(net_manifest).read()
				
				try:
					mf = Manifest.Manifest()
					mf.read_manifest(net_manifest_data)
					
					self.known_servers[mf.servername] = [mf.address, mf.manifestaddress, -1, mf.addonline]
					
					self.update_listctrl()
					
					self.progbar.SetValue(100)
					self.statusbar.SetStatusText('Manifest added.')
						
					self.write_config()
				except:
					wx.MessageBox("Unable to open manifest file. It may be malformed or corrupted -- try downloading it again.", "Error", wx.OK | wx.ICON_ERROR)
					raise
				
			except:
				wx.MessageBox("Unable to download manifest file. Check your URL and try again.", "Error", wx.OK | wx.ICON_ERROR)
				raise
			finally:
				self.unblock_ui()
		
	def on_selectexe(self, e):
		filedialog = wx.FileDialog(self, message = "Select your ArmA 2 or ArmA 2 OA executable file", defaultDir = os.getcwd(), wildcard = "ArmA 2 Executable (*.exe)|*.exe", style = wx.FD_OPEN)
		
		if filedialog.ShowModal() == wx.ID_OK:
			self.arma_exe = filedialog.GetPath()
			self.write_config()
			
	def on_configexe(self, e):
		configdialog = ConfigExeDialog(self.arma_flags, self.arma_custommods, None)
		
		if configdialog.ShowModal() == wx.ID_OK: # This isn't standard behavior but meh...
			result = configdialog.GetResult()
			print result
			self.arma_flags = result[0]
			self.arma_custommods = result[1]
			self.write_config()
		
	def on_selectdir(self, e):
		dirdialog = wx.DirDialog(self, message = "Select directory to save addons to after they are downloaded. This should usually be your ArmA 2 install directory.", defaultPath = os.getcwd())
		
		if dirdialog.ShowModal() == wx.ID_OK:
			self.addon_directory = dirdialog.GetPath()
			try:
				os.chdir(self.addon_directory)
			except:
				print "ERROR: Unable to change to config addon directory. Using current directory."
				
			self.write_config()
	
	def on_about(self, e):
		about = wx.AboutDialogInfo()
		about.AddDeveloper("Ryan Schultz")
		about.SetDescription("TaoSync is a program for synchronizing ArmA 2 addons online.")
		#about.SetIcon(wx.Bitmap("taosync.png"))
		about.SetLicense("See LICENSE.txt")
		about.SetName("TaoSync")
		about.SetCopyright("(C) 2012 Ryan Schultz")
		wx.AboutBox(about)
		
	def on_check(self, e):
		if len(self.known_servers) > 0:
			self.block_ui()
			self.progbar.SetValue(2)
			self.StatusBar.SetStatusText("Checking servers for updates...")
			
			check_thread = threading.Thread(target = self.check)
			check_thread.start()

			wait_thread = threading.Thread(target = self.wait_to_finish_check, args=(check_thread,)) # Leave that comma.
			wait_thread.start()
		else:
			wx.MessageBox("You must add a server before checking.", "Warning", wx.OK | wx.ICON_WARNING)
		
	
	def check(self):
		inc = (100 / len(self.known_servers)) / 2
		# Divide by two because each server has two separate increments: download, process
		curval = 0
		
		self.is_checking = True
		
		for server, data in self.known_servers.items():
			server_name = server
			server_manifestaddress = data[1]
			try:
				net_manifest_data = urllib2.urlopen(server_manifestaddress).read()
				
				self.progbar.SetValue(curval+inc)
				curval += inc
				
				try:
					old_dir = os.getcwd()
					os.chdir(self.addon_directory)
					
					mf = Manifest.Manifest()
					mf.read_manifest(net_manifest_data)
					
					self.ts = TaoSync.TaoSync(server_manifestaddress)
					checkresult = self.ts.sync(checkonly = True)
					
					if checkresult == True:
						self.known_servers[mf.servername] = [mf.address, mf.manifestaddress, 0, mf.addonline]
					elif checkresult == False:
						self.known_servers[mf.servername] = [mf.address, mf.manifestaddress, 1, mf.addonline]
						
					self.progbar.SetValue(curval+inc)
					curval += inc
					
					self.update_listctrl()
						
				except:
					wx.MessageBox("Unable to process manifest file for %s. It may have been corrupted -- try downloading it again." % server_name, "Error", wx.OK | wx.ICON_ERROR)
					raise
				finally:
					os.chdir(old_dir)
					
			except:
				wx.MessageBox("Unable to download manifest for %s. The address may have changed. Try downloading and reloading the manifest manually." % server_name, "Error", wx.OK | wx.ICON_ERROR)	
				raise
			
	def wait_to_finish_check(self, check_thread):
		check_thread.join()
		# Will block until sync finishes
		
		self.progbar.SetValue(100)
		self.statusbar.SetStatusText("Checking for updates finished.")
		
		self.write_config()
		
		self.is_checking = False
		self.unblock_ui()
	
				
	def on_sync(self, e):
		selected = self.server_listctrl.GetFirstSelected()
		
		if selected != -1: # -1 means nothing selected
			# Get the data map
			map_idx = self.server_listctrl.GetItemData(selected)
			# Get the server info from the data map
			server, data = self.server_listctrl_datamapping[map_idx]
			server_name = server
			server_manifestaddress = data[1]
			
			self.current_server = server_name
			
			self.block_ui()
			
			self.progbar.SetValue(2)
			self.statusbar.SetStatusText("Syncing %s..." % server_name)
			
			try:
				old_dir = os.getcwd()
				os.chdir(self.addon_directory)
				sync_thread = threading.Thread(target=self.sync, args=(server_manifestaddress,))
				sync_thread.name = "Sync"
				sync_thread.start()
				
				wait_thread = threading.Thread(target=self.wait_to_finish_sync, args = (sync_thread,)) # Leave that comma.
				wait_thread.name = "Wait"
				wait_thread.start()
				
			except:
				wx.MessageBox("Error while syncing %s." % server_name, "Error", wx.OK | wx.ICON_ERROR)
				raise
			finally:
				os.chdir(old_dir)
		else:
			wx.MessageBox("No server selected. Please select one from the list, or add a manifest.", "Warning", wx.OK | wx.ICON_WARNING)
	
	def sync(self, server_manifestaddress):
		self.ts = TaoSync.TaoSync(server_manifestaddress) #1 is the manifest address
		self.is_syncing = True
		
		bar_thread = threading.Thread(target = self.update_sync_progress_bar)
		bar_thread.name = "Bar watcher"
		bar_thread.start()
		
		try:
			self.ts.sync()
		
			self.known_servers[self.current_server][2] = 1 # Set sync complete status
		
			self.update_listctrl()
		except:
			print "WARN: Sync interrupted."
		
	def wait_to_finish_sync(self, sync_thread):
		sync_thread.join()
		# Will block until sync finishes
		self.ts = None
		
		try:
			self.progbar.SetValue(100)
			self.statusbar.SetStatusText("Sync finished for %s." % self.current_server)
			
			self.known_servers[self.current_server][2] = 1 # Mark the status as sync'd.
			
			self.is_syncing = False
			self.unblock_ui()
		except:
			print "WARN: Waiting to finish sync interrupted."
		
	def update_sync_progress_bar(self):
		try:
			while self.is_syncing and self.ts:
				self.progbar_dl.SetValue(self.ts.download_percent)
				self.statusbar.SetStatusText("Downloading %s ..." % self.ts.download_file)
				time.sleep(1)
		except:
			print "INFO: Lost the GUI, quitting."
	
	def on_launch(self, e):
		selected = self.server_listctrl.GetFirstSelected()
		
		if selected != -1: # -1 means nothing selected
			# Get the data map
			map_idx = self.server_listctrl.GetItemData(selected)
			# Get the server info from the data map
			server, data = self.server_listctrl_datamapping[map_idx]
			server_name = server
			
			if os.path.isfile(self.arma_exe):
				try:
					# Build command line flags
					modline = " -mod=" + '"' + self.known_servers[server_name][3] + self.arma_custommods + '"'# Get the addons property, which is already
					params = [self.arma_flags] + [modline]
					# formated to use here
					print params
					
					pid = Popen([self.arma_exe, params], shell = True).pid
				except:
					wx.MessageBox("Unable to launch ArmA 2 executable at %s." % self.arma_exe, "Error", wx.OK | wx.ICON_ERROR)
					raise
			else:
				wx.MessageBox("No file at %s. Please set the correct path for your ArmA 2 executable." % self.arma_exe, "Error", wx.OK | wx.ICON_ERROR)
		else:
			wx.MessageBox("No server selected. Please select one from the list, or add a manifest.", "Warning", wx.OK | wx.ICON_WARNING)
	
	def update_listctrl(self):
		""" Update the servers in the listctrl to match the data in self.known_servers. """
		self.server_listctrl_datamapping = []
		# print "Updating list ctrl: ", self.known_servers
		for idx, server in enumerate(self.known_servers.items()):
			self.server_listctrl_datamapping.append(server) # The server names will now be in the same order in the mapping as they are in the list
			server_name, data = server[0], server[1]
			server_status = data[2]
			server_addons = data[3]
			if self.server_listctrl.FindItem(-1, server_name) == -1:
				# Insert server name
				self.server_listctrl.InsertStringItem(idx, server_name)
			else:
				# It's already there, set it
				self.server_listctrl.SetStringItem(idx, 0, server_name)
			
			# Set updated status
			if server_status == -1:
				self.server_listctrl.SetStringItem(idx, 1, "??")
			elif server_status == 0:
				self.server_listctrl.SetStringItem(idx, 1, "No")
				self.server_listctrl.SetItemTextColour(idx, wx.RED)
			elif server_status == 1:
				self.server_listctrl.SetStringItem(idx, 1, "Yes")
				self.server_listctrl.SetItemTextColour(idx, wx.GREEN)
				
			# Set the addons list
			self.server_listctrl.SetStringItem(idx, 2, server_addons)
			# Set item data to point to the index in the mapping for the known_servers dict
			self.server_listctrl.SetItemData(idx, idx)
			
			
	def block_ui(self):
		self.ui_blocked = True
		self.menubar.EnableTop(pos = 0, enable = False)
		self.menubar.EnableTop(pos = 1, enable = False)
		self.menubar.EnableTop(pos = 2, enable = False)
		self.toolbar.Enable(False)
		self.server_listctrl.Enable(False)
		
	def unblock_ui(self):
		self.ui_blocked = False
		self.menubar.EnableTop(pos = 0, enable = True)
		self.menubar.EnableTop(pos = 1, enable = True)
		self.menubar.EnableTop(pos = 2, enable = True)
		self.toolbar.Enable(True)
		self.server_listctrl.Enable(True)
		
	
	def read_config(self):
		# Read our configuation files
		sync_config = ConfigParser.RawConfigParser()
		server_config = ConfigParser.RawConfigParser()
		
		# Read the TaoSync config
		try:
			with open(os.path.join(os.getcwd(), "taosync.cfg")) as cfg_file:
				sync_config.readfp(cfg_file)
				
			self.addon_directory = sync_config.get("TaoSync","AddonDirectory")
			self.arma_exe = sync_config.get("TaoSync","ArmAExe")
			self.arma_flags = sync_config.get("TaoSync","ArmAFlags")
			self.arma_custommods = sync_config.get("TaoSync","ArmACustomMods")
		except:
			print "WARN: Couldn't read sync config file. Using defaults."
			#raise
			# It's okay, defaults are fine for now.
			
		# Read the server config
		try:
			with open(os.path.join(os.getcwd(), "taosync_serverlist.cfg")) as cfg_file:
				server_config.readfp(cfg_file)
			
			for server in server_config.sections():
				# [mf.address, "file:////" + mf.manifestaddress]
				server_address = server_config.get(server, "Address")
				server_manifest_address = server_config.get(server, "ManifestAddress")
				server_addons = server_config.get(server, "Addons")
				self.known_servers[server] = [server_address, server_manifest_address, -1, server_addons] # Final value is updated status, not tracked in cfg
		except:
			print "WARN: Couldn't read server list file. No servers loaded to list."
			#raise
			
	def write_config(self):
		# Write our configuation files
		sync_config = ConfigParser.RawConfigParser()
		server_config = ConfigParser.RawConfigParser()
		
		# Write the TaoSync config
		try:
			try:
				with open(os.path.join(os.getcwd(), "taosync.cfg")) as cfg_file:
					sync_config.readfp(cfg_file)
			except:
				print "INFO: No sync config exists, making new one."
				
			if not sync_config.has_section("TaoSync"):
				sync_config.add_section("TaoSync")
			sync_config.set("TaoSync","AddonDirectory", self.addon_directory)
			sync_config.set("TaoSync","ArmAExe", self.arma_exe)
			sync_config.set("TaoSync","ArmAFlags", self.arma_flags)
			sync_config.set("TaoSync","ArmACustomMods", self.arma_custommods)
			
			sync_config.write(open(os.path.join(os.getcwd(), "taosync.cfg"), 'wb'))
		except:
			print "WARN: Couldn't write sync config file."
			raise
			
		# Write the server config
		try:
			old_dir = os.getcwd()
			os.chdir(self.run_dir)
			
			try:
				with open(os.path.join(os.getcwd(), "taosync_serverlist.cfg")) as cfg_file:
					server_config.readfp(cfg_file)
			except:
				print "INFO: No server config exists, making new one."
			
			print "Known servers: ", self.known_servers
			if len(self.known_servers) > 0:
				for server, data in self.known_servers.items():
					server_name = server
					server_address = data[0]
					server_manifestaddress = data[1]
					# server_updatestatus = self.known_servers[server][2]
					server_addons = data[3]
					if not server_config.has_section(server_name):
						server_config.add_section(server_name)
					server_config.set(server_name,"Address",server_address)
					server_config.set(server_name,"ManifestAddress",server_manifestaddress)
					server_config.set(server_name,"Addons",server_addons)
			# Delete any entries that no longer exist
			for section in server_config.sections():
				if section not in self.known_servers:
					print "INFO: Removed old entry %s" % section
					server_config.remove_section(section)
				
			server_config.write(open(os.path.join(os.getcwd(), "taosync_serverlist.cfg"), 'wb'))
		except:
			print "WARN: Couldn't write server config file."
			raise
		finally:
			os.chdir(old_dir)
		
		
	def on_buildrepo(self, e):

		repodialog = BuildRepoDialog(None)
		
		repodialog.ShowModal()	
			
if __name__ == "__main__":
	app = wx.App(redirect = False)
	
	f = TaoSyncGUI(None, title='TaoSync', style = wx.SYSTEM_MENU | wx.RESIZE_BORDER | wx.CAPTION | wx.MINIMIZE_BOX | wx.CLOSE_BOX, size = (600,367))
	
	# f = TaoSyncGUI(None)
	f.Show()

	app.MainLoop()